package com.video.upload.controller;


import cn.hutool.core.map.FuncKeyMap;
import com.video.common.result.ResponseResult;
import com.video.upload.config.MinioProperties;
import com.video.upload.service.IVideoListService;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.messages.DeleteObject;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.Null;
import java.io.*;
import java.util.ArrayList;
import java.util.List;


/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 威仔
 * @since 2024-11-08
 */
@RestController
@RequestMapping("/video-list")
@RequiredArgsConstructor
public class VideoListController {

    @Autowired
    private IVideoListService videoListService;
    @Autowired
    private MinioClient minioClient;
    @Autowired
    MinioProperties minioProperties;



    /**
     * 视频切片上传
     */
    @PostMapping("/chunk")
    public ResponseResult uploadChunk(@RequestParam("file") MultipartFile file,
                              @RequestParam("chunkIndex") int chunkIndex,
                              @RequestParam("chunkTotal") int chunkTotal,
                              @RequestParam("identifier") String identifier) throws Exception {
        InputStream inputStream = file.getInputStream();
        try {
            String objectName = identifier + "/" + chunkIndex;
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            return ResponseResult.okResult("Chunk " + chunkIndex + " uploaded successfully.");
        } catch (MinioException | IOException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(1006,"Error: " + e.getMessage());
        }finally {
            inputStream.close();
        }
    }

    /**
     * 合并切片上传
     * @param identifier
     * @param chunkTotal
     * @return
     */
    @PostMapping("/merge")
    public ResponseResult mergeChunks(@RequestParam("identifier") String identifier,
                              @RequestParam("chunkTotal") int chunkTotal) throws Exception {
        try {
            List<ComposeSource> sources = new ArrayList<>();
            for (int i = 0; i < chunkTotal; i++) {
                sources.add(ComposeSource.builder()
                        .bucket(minioProperties.getBucket())
                        .object(identifier + "/" + i)
                        .build());
            }
            // 合并切片视频
            String mergedFileName = "video_list/" + identifier ;
            minioClient.composeObject(
                    ComposeObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(mergedFileName)
                            .sources(sources)
                            .build());
            // 删除文件的参数
            for (ComposeSource source : sources){
                minioClient.removeObject( RemoveObjectArgs.builder().bucket(minioProperties.getBucket())
                        .object(source.object())
                        .build());
            }

            // 下载合并后的视频文件
            File localFile = null;
            try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(minioProperties.getBucket())
                    .object(mergedFileName)
                    .build())) {
                // 指定要创建的文件夹路径
                String directoryPath = "D:/video/" + identifier.replace(".mp4","");
                // 创建 File 对象
                File directory = new File(directoryPath);
                // 创建文件夹
                boolean isCreated = directory.mkdirs(); // 使用 mkdirs() 方法可以创建多级目录
                // 保存到本地文件
                localFile=   new File(directoryPath +"/" + identifier); // 确保路径有效

                try (OutputStream outputStream = new FileOutputStream(localFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                }
            }

            //删除minio中合并文件
            minioClient.removeObject( RemoveObjectArgs.builder().bucket(minioProperties.getBucket())
                    .object("video_list/" +identifier)
                    .build());

            // 转换合并后的 MP4 文件为 M3U8 格式
            convertMp4ToM3U8(localFile);


            //上传m3u8视频到minio
            uploadM3U8Files("D:/video/" +identifier.replace(".mp4", ""), identifier.replace(".mp4", ""));

            // 清理本地文件
            localFile.delete();
            // 删除本地文件
            File outputDir = new File("D:/video/" + identifier.replace(".mp4", ""));
            deleteDirectory(outputDir);
            String Url = minioProperties.getReadPath()+"/"+minioProperties.getBucket() +"/"+identifier.replace(".mp4", "")+ "/" + identifier.replace(".mp4", "")+".m3u8";
            return ResponseResult.okResult(Url);
        } catch (MinioException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(1006,"Error: " + e.getMessage());
        }
    }


    /**
     * FFmpeg 转换函数
     * @param inputFile
     * @throws IOException
     * @throws InterruptedException
     */
    private void convertMp4ToM3U8(File inputFile) throws IOException, InterruptedException {
        String outputPath = inputFile.getAbsolutePath().replace(".mp4", ""); // 输出路径去掉后缀
        ProcessBuilder processBuilder = new ProcessBuilder(
                "ffmpeg", "-i", inputFile.getAbsolutePath(),
                "-codec", "copy", "-start_number", "0",
                "-hls_time", "10", "-hls_list_size", "0",
                "-f", "hls", outputPath + ".m3u8"
        );

        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        // 读取 FFmpeg 输出，并打印进度
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // 打印 FFmpeg 输出
                // 解析并打印进度信息
                if (line.contains("frame=")) {
                    String[] parts = line.split(" ");
                    for (String part : parts) {
                        if (part.startsWith("time=")) {
                            System.out.println("当前进度: " + part); // 打印当前时间进度
                        }
                    }
                }
            }
        }
        // 等待 FFmpeg 进程完成
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            throw new IOException("FFmpeg 转换失败，退出码：" + exitCode);
        }
    }


    /**
     *  上传 M3U8 文件和 ts 片段
     * @param outputDir
     * @param videoName
     */
    private void uploadM3U8Files(String outputDir, String videoName) {
        File folder = new File(outputDir);
        System.out.println("输出目录: " + folder.getAbsolutePath());
        File[] filesToUpload = folder.listFiles();
        System.out.println("文件数量: " + (filesToUpload != null ? filesToUpload.length : 0));

        if (filesToUpload != null) {
            for (File file : filesToUpload) {
                try {
                    // 生成在 MinIO 中的对象名，以视频名称作为文件夹
                    String objectName = videoName + "/" + file.getName();
                    System.out.println("准备上传: " + objectName);
                    minioClient.putObject(PutObjectArgs.builder()
                            .bucket(minioProperties.getBucket())
                            .object(objectName)
                            .stream(new FileInputStream(file), file.length(), -1)
                            .build());
                    System.out.println("上传成功: " + objectName);
                }  catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 删除文件
     */
    private void deleteDirectory(File directory) {
        if (directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile()) {
                        boolean deleted = file.delete(); // 删除文件
                        if (deleted) {
                            System.out.println("已删除文件: " + file.getAbsolutePath());
                        } else {
                            System.out.println("无法删除文件: " + file.getAbsolutePath());
                        }
                    }
                }
            }
        }
    }


}

